#include "GeneralLibrary/Linkage.h"

/*------- 这里是为进入内核做最后的准备，因为内核基本为C语音开发，所以把需要大量用到汇编语言的代码都放在这里  ---------------------------------*/
.section .text              //将这段代码编译为代码段或代码子段

.globl  _start              //这里必须将 _start 声明为全局变量，否则会报错

ENTRY(_start)                      //使用这个标识符，就等于告诉编译器这里是程序的入口
//1. 将段寄存器和栈指针初始化
    mov     $0x10,      %ax
    mov     %ax,        %ds
    mov     %ax,        %es
    mov     %ax,        %fs
    mov     %ax,        %gs
    mov     %ax,        %ss
    mov     $0x7E00,    %esp

//2. 将 GDT 表的指针加载到 GDTR 寄存器
    lgdt    GDT_POINTER(%rip)

//3. 将 IDT 表的指针加载到 IDTR 寄存器里面
    lidt    IDT_POINTER(%rip)

    mov	    $0x10,	%ax
    mov	    %ax,	%ds
    mov	    %ax,	%es
    mov	    %ax,	%fs
    mov	    %ax,	%gs
    mov	    %ax,	%ss

    movq	_stack_start(%rip),	%rsp

//4. 将初始化好的页表的页目录加载到 CR3 寄存器里面
    movq    $0x101000,  %rax            //0x100000是内核在物理地址中的偏移，0x1000是
    movq    %rax,       %cr3
    movq    switchSeg(%rip),   %rax     //把
    pushq   $0x08                       //把段选择子压入栈
    pushq   %rax                        //把段内地址压入栈
    lretq                               //这个指令可以实现段间远跳转，但是需要往栈中依次压入段选择子和段内地址

//5. 将进入内核的最后一段代码的地址存放到内存
switchSeg:
    .quad   entry64

entry64:
//6. 再次初始化段寄存器和栈指针
    movq    $0x10,      %rax
    movq    %rax,       %ds
    movq    %rax,       %es
    movq    %rax,       %fs
    movq    %rax,       %gs
    movq    %rax,       %ss
    movq    _stack_start(%rip),    %rsp    //这个是映射后的虚拟地址

// 判断是不是 APU 在执行，就调转到SMP那
    movq	$0x1b,	    %rcx		//if APU
    rdmsr
    bt	    $8,	        %rax
    jnc	    startSmp

// 7. 设置 IDT
setupIDT:
    leaq    ignoreInt(%rip),    %rdx
    movq    $(0x08 << 16),      %rax        // 0x08 << 16 = 0x0008 0000
    movw    %dx,        %ax
    movq    $(0x8E00 << 32),    %rcx        // 0x8E00 << 32 = 0x8E00 0000 0000
    addq    %rcx,       %rax                // RAX = 0x00008E00_0x00080000
    movl    %edx,       %ecx
    shrl    $16,        %ecx                // 保存 ECX 的高16位，低16位抛弃，16位是 0 - 65535
    shlq    $48,        %rcx                // 把 ECX 的低16位移到 RCX 的高16位处
    addq    %rcx,       %rax                // RAX = 0x****8E00_0x00080000 中断描述符的低 8 字节搞定
    shrq    $32,        %rdx                // 抛弃 RDX 的低32位
    leaq    IDT_TABLE(%rip),    %rdi
    mov     $256,       %rcx

cirSetIDT:
    movq    %rax,       (%rdi)              // 低8字节先赋值
    movq    %rdx,       8(%rdi)             // 再赋值高8字节
    addq    $0x10,      %rdi                // 移动到下个中断描述符的首地址处
    dec     %rcx
    jne     cirSetIDT                       // 如果上一句指令减到了0，这里就不执行了

// 8. 设置 TSS
setupTSS64:
    leaq    initTss(%rip),  %rdx
    xorq    %rax,       %rax                // 清空 RAX
    xorq    %rcx,       %rcx                // 清空 RCX
    movq    $0x89,      %rax                // RAX = 0x00000000_0x00000089
    shlq    $40,        %rax                // RAX = 0x00008900_0x00000000
    movl    %edx,       %ecx
    shrl    $24,        %ecx                // 这里只保留了 ECX 的高 8 位，也就是一个字节
    shlq    $56,        %rcx                // 将刚才保留的一个字节移到 RCX 的最高的那一个字节
    addq    %rcx,       %rax                // RAX = 0x**008900_0x00000000
    xorq    %rcx,       %rcx                // 清空 RCX
    movl    %edx,       %ecx
    andl    $0xFFFFFF,  %ecx                // 清空 ECX 的高8位
    shlq    $16,        %rcx                //
    addq    %rcx,       %rax                //
    addq    $103,       %rcx
    leaq    GDT_TABLE(%rip),    %rdi
    movq    %rax,       80(%rdi)            // TSS 段的偏移
    shrq    $32,        %rdx
    movq    %rdx,       88(%rdi)            // TSS+1 段的偏移

//9. 准备跳到内核
    movq    goToKernel(%rip),     %rax
    pushq   $0x08
    pushq   %rax
    lretq                                   //这条指令执行完后，就会跳到内核程序的入口了

//10. 这里放着内核的入口地址
goToKernel:
    .quad   startKernel

// 11. 如果是APU在执行
startSmp:
	movq	goToSmpKernel(%rip),	%rax		/* movq address */
	pushq	$0x08
	pushq	%rax
	lretq

// 这里放的是 SMP 的开始代码
goToSmpKernel:
	.quad	StartSMP

// 忽略所有的中断
ignoreInt:
    // 1> 关中断
    cld

    // 2> 保存现场
    pushq   %rax
    pushq   %rbx
    pushq   %rcx
    pushq   %rdx
    pushq   %rbp
    pushq   %rdi
    pushq   %rsi

    pushq	%r8
    pushq	%r9
    pushq	%r10
    pushq	%r11
    pushq	%r12
    pushq	%r13
    pushq	%r14
    pushq	%r15

    movq    %es,        %rax
    pushq   %rax
    movq    %ds,        %rax
    pushq   %rax

    // 3> 初始化 DS 和 ES 段寄存器
    movq    $0x10,      %rax
    movq    %rax,       %ds
    movq    %rax,       %es

    // 4> 传参，调用打印字体函数
    leaq    intMsg(%rip),  %rax
    pushq   %rax
    movq    %rax,       %rdx
    movq    $0x00000000,    %rsi        // 背景色
    movq    $0x00FF0000,    %rdi        // 字体色
    movq    $0,         %rax            // 重置 RAX
    callq   printfColor
    addq    $0x8,       %rsp

Loop:
	jmp	Loop

	popq	%rax
	movq	%rax,	%ds
	popq	%rax
	movq	%rax,	%es

	popq	%r15
	popq	%r14
	popq	%r13
	popq	%r12
	popq	%r11
	popq	%r10
	popq	%r9
	popq	%r8

	popq	%rsi
	popq	%rdi
	popq	%rbp
	popq	%rdx
	popq	%rcx
	popq	%rbx
	popq	%rax
	iretq

intMsg:
	.asciz "Unknown Interrupt or Fault at RIP\n"

// 这里是默认的栈指针
ENTRY(_stack_start)
	.quad	initTaskUnion + 32768                   // initTaskUnion 写在 Task 文件里面

/*----------- 初始化页表，这里采用的是3级页表，三级的页面大小是2MB --------*/
.align 8               //将对齐标准设置为8字节

.org    0x1000          //这当前的代码编译到当前程序偏移的 0x1000 处，这里可以当做是页目录的地址

_PML4E:                 //这个是第一级页表，表里面的值就是 CR3 寄存器放的值 47 - 39
    .quad   0x102003    // 100000 是内核的基地址
    .fill   255,8,0
    .quad   0x102003
    .fill   255,8,0

.org    0x2000          //这是将当前的代码编译到当前程序偏移的 0x2000 处

_PDPTE:                 //这个是第二级页表，只能由第一级页表里面的一个条目的38 - 30位来指定的
    .quad   0x103003
    .fill   511,8,0

.org    0x3000          //这是将当前的代码编译到当前程序偏移的 0x3000 处

_PDE:                   //这个是第三级页表，只能由第二级页表里面的一个条目的29-21位来指定
    .quad	0x000083        /* 这里代表物理地址0x000000 */
    .quad	0x200083
    .quad	0x400083
    .quad	0x600083
    .quad	0x800083		/* 0x800083 */
    .quad	0xa00083
    .quad	0xc00083
    .quad	0xe00083
    .quad	0x1000083
    .quad	0x1200083
    .quad	0x1400083
    .quad	0x1600083
    .quad	0x1800083
    .quad	0x1a00083
    .quad	0x1c00083
    .quad	0x1e00083
    .quad	0x2000083
    .quad	0x2200083
    .quad	0x2400083
    .quad	0x2600083
    .quad	0x2800083
    .quad	0x2a00083
    .quad	0x2c00083
    .quad	0x2e00083

    .quad	0xe0000083		/*0x 3000000*/
    .quad	0xe0200083
    .quad	0xe0400083
    .quad	0xe0600083
    .quad	0xe0800083
    .quad	0xe0a00083
    .quad	0xe0c00083
    .quad	0xe0e00083
    .fill	480,8,0

.section .data      /* 表示这个段是数据段 */

//----------------  下面的是 GDT 表的结构 --------------------------
//注：GDT 是全局描述符表的缩写

.globl  GDT_TABLE   /* 将 GDT_TABLE 声明为全局变量，外部也可以访问它 */

/* 这里定义的是 GDT 表的描述符 */
GDT_TABLE:
    .quad   0x0000000000000000  /* 0、NULL 的描述符，这个是规定，偏移：00*/
    .quad   0x0020980000000000  /* 1、内核层代码段64位描述符，偏移：08 */
    .quad   0x0000920000000000  /* 2、内核层数据段64位描述符，偏移：10 */
    .quad	0x0000000000000000	/* 3、用户层代码段32位描述符，偏移：18 */
    .quad	0x0000000000000000	/* 4、用户层数据段32位描述符，偏移：20 */
    .quad   0x0020f80000000000  /* 3、用户层代码段64位描述符，偏移：28 */
    .quad   0x0000f20000000000  /* 4、用户层数据段64位描述符，偏移：30 */
    .quad   0x00cf9a000000ffff  /* 5、内核层代码段32位描述符，偏移：38 */
    .quad   0x00cf92000000ffff  /* 6、内核层数据段32位描述符，偏移：40 */
    .fill   100,8,0              /* 再填充100个空的描述符 */
GDT_END:

GDT_POINTER:                                        /* GDT表的指针 */
GDT_LIMIT:      .word   GDT_END - GDT_TABLE - 1     /* GDT表的限制长度，减去一是因为长度从0开始 */
GDT_BASE:       .quad   GDT_TABLE                   /* GDT表的基址 */

//---------------  下面的是 IDT 表的结构  -------------------------
//注：IDT 是中断描述符表的缩写

.globl  IDT_TABLE   /* 将 IDT_TABLE 声明为全局变量，外部也可以访问它 */

/* 这里定义的是 IDT 表的描述符 */
IDT_TABLE:
    .fill   512,8,0             /* 给 IDT 表填充256个空的描述符 */
IDT_END:

IDT_POINTER:                                        /* IDT表的指针 */
IDT_LIMIT:      .word   IDT_END - IDT_TABLE - 1     /* IDT表的限制长度 */
IDT_BASE:       .quad   IDT_TABLE                   /* IDT表的基址 */

//---------------   下面是 TSS 表的结构  -------------------------
//注：TSS 是任务状态段的缩写

//.globl  TSS64_TABLE /* 将 TSS64_TABLE 声明为全局变量，外部也可以访问它 */

/* 这里定义的是 TSS 表的描述符 */
//TSS64_TABLE:
//    .fill   13,8,0              /* 给 TSS 表填充13个空的描述符 */
//TSS64_END:
